home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / shells / tcshsrc.zoo / tcsh / sh.file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-25  |  19.4 KB  |  817 lines

  1. /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/sh.file.c,v 3.1 1991/07/15 19:37:24 christos Exp $ */
  2. /*
  3.  * sh.file.c: File completion for csh. This file is not used in tcsh.
  4.  */
  5. /*-
  6.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions
  11.  * are met:
  12.  * 1. Redistributions of source code must retain the above copyright
  13.  *    notice, this list of conditions and the following disclaimer.
  14.  * 2. Redistributions in binary form must reproduce the above copyright
  15.  *    notice, this list of conditions and the following disclaimer in the
  16.  *    documentation and/or other materials provided with the distribution.
  17.  * 3. All advertising materials mentioning features or use of this software
  18.  *    must display the following acknowledgement:
  19.  *    This product includes software developed by the University of
  20.  *    California, Berkeley and its contributors.
  21.  * 4. Neither the name of the University nor the names of its contributors
  22.  *    may be used to endorse or promote products derived from this software
  23.  *    without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. #ifdef FILEC
  38. #include "config.h"
  39. RCSID("$Id: sh.file.c,v 3.1 1991/07/15 19:37:24 christos Exp $")
  40.  
  41. #include "sh.h"
  42.  
  43. /*
  44.  * Tenex style file name recognition, .. and more.
  45.  * History:
  46.  *    Author: Ken Greer, Sept. 1975, CMU.
  47.  *    Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
  48.  */
  49.  
  50. #define ON    1
  51. #define OFF    0
  52. #ifndef TRUE
  53. #define TRUE 1
  54. #endif
  55. #ifndef FALSE
  56. #define FALSE 0
  57. #endif
  58.  
  59. #define ESC    '\033'
  60.  
  61. typedef enum {
  62.     LIST, RECOGNIZE
  63. }       COMMAND;
  64.  
  65. static    void     setup_tty        __P((int));
  66. static    void     back_to_col_1        __P((void));
  67. static    void     pushback        __P((Char *));
  68. static    void     catn            __P((Char *, Char *, int));
  69. static    void     copyn            __P((Char *, Char *, int));
  70. static    Char     filetype        __P((Char *, Char *));
  71. static    void     print_by_column    __P((Char *, Char *[], int));
  72. static    Char     *tilde            __P((Char *, Char *));
  73. static    void     retype            __P((void));
  74. static    void     beep            __P((void));
  75. static    void      print_recognized_stuff    __P((Char *));
  76. static    void     extract_dir_and_name    __P((Char *, Char *, Char *));
  77. static    Char    *getentry        __P((DIR *, int));
  78. static    void     free_items        __P((Char **));
  79. static    int     tsearch        __P((Char *, COMMAND, int));
  80. static    int     recognize        __P((Char *, Char *, int, int));
  81. static    int     is_prefix        __P((Char *, Char *));
  82. static    int     is_suffix        __P((Char *, Char *));
  83. static    int     ignored        __P((Char *));
  84.  
  85.  
  86. /*
  87.  * Put this here so the binary can be patched with adb to enable file
  88.  * completion by default.  Filec controls completion, nobeep controls
  89.  * ringing the terminal bell on incomplete expansions.
  90.  */
  91. bool    filec = 0;
  92.  
  93. static void
  94. setup_tty(on)
  95.     int     on;
  96. {
  97. #ifdef TERMIO
  98. # ifdef POSIX
  99.     static struct termios tchars;
  100. # else
  101.     static struct termio tchars;
  102. # endif /* POSIX */
  103.  
  104.     if (on) {
  105. # ifdef POSIX
  106.     (void) tcgetattr(SHIN, &tchars);
  107. # else
  108.         (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars);
  109. # endif /* POSIX */
  110.     tchars.c_cc[VEOL] = ESC;
  111.     if (tchars.c_lflag & ICANON)
  112. # ifdef POSIX
  113.         on = TCSANOW;
  114. # else
  115.         on = TCSETAW;
  116. # endif /* POSIX */
  117.     else {
  118. # ifdef POSIX
  119.         on = TCSAFLUSH;
  120. # else
  121.         on = TCSETAF;
  122. # endif /* POSIX */
  123.         tchars.c_lflag |= ICANON;
  124.     
  125.     }
  126. #ifdef POSIX
  127.         (void) tcsetattr(SHIN, on, &tchars);
  128. #else
  129.         (void) ioctl(SHIN, on, (ioctl_t) &tchars);
  130. #endif /* POSIX */
  131.     }
  132.     else {
  133.     tchars.c_cc[VEOL] = _POSIX_VDISABLE;
  134. # ifdef POSIX
  135.     (void) tcsetattr(SHIN, TCSANOW, &tchars);
  136. # else
  137.         (void) ioctl(SHIN, TCSETAW, (ioctl_t) &tchars);
  138. # endif /* POSIX */
  139.     }
  140. #else
  141.     struct sgttyb sgtty;
  142.     static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */
  143.  
  144.     if (on) {
  145.     (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars);
  146.     tchars.t_brkc = ESC;
  147.     (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  148.     /*
  149.      * This must be done after every command: if the tty gets into raw or
  150.      * cbreak mode the user can't even type 'reset'.
  151.      */
  152.     (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty);
  153.     if (sgtty.sg_flags & (RAW | CBREAK)) {
  154.         sgtty.sg_flags &= ~(RAW | CBREAK);
  155.         (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty);
  156.     }
  157.     }
  158.     else {
  159.     tchars.t_brkc = -1;
  160.     (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  161.     }
  162. #endif /* TERMIO */
  163. }
  164.  
  165. /*
  166.  * Move back to beginning of current line
  167.  */
  168. static void
  169. back_to_col_1()
  170. {
  171. #ifdef TERMIO
  172. # ifdef POSIX
  173.     struct termios tty, tty_normal;
  174. # else
  175.     struct termio tty, tty_normal;
  176. # endif /* POSIX */
  177. #else
  178.     struct sgttyb tty, tty_normal;
  179. #endif /* TERMIO */
  180.  
  181. # ifdef BSDSIGS
  182.     sigmask_t omask = sigblock(sigmask(SIGINT));
  183. # else
  184.     sighold(SIGINT);
  185. # endif /* BSDSIGS */
  186.  
  187. #ifdef TERMIO
  188. # ifdef POSIX
  189.     (void) tcgetattr(SHOUT, &tty);
  190. # else
  191.     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal);
  192. # endif /* POSIX */
  193.     tty_normal = tty;
  194.     tty.c_iflag &= ~INLCR;
  195.     tty.c_oflag &= ~ONLCR;
  196. # ifdef POSIX
  197.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  198. # else
  199.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  200. # endif /* POSIX */
  201.     (void) write(SHOUT, "\r", 1);
  202. # ifdef POSIX
  203.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  204. # else
  205.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  206. # endif /* POSIX */
  207. #else
  208.     (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty);
  209.     tty_normal = tty;
  210.     tty.sg_flags &= ~CRMOD;
  211.     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty);
  212.     (void) write(SHOUT, "\r", 1);
  213.     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal);
  214. #endif /* TERMIO */
  215.  
  216. # ifdef BSDSIGS
  217.     (void) sigsetmask(omask);
  218. # else
  219.     (void) sigrelse(SIGINT);
  220. # endif /* BSDISGS */
  221. }
  222.  
  223. /*
  224.  * Push string contents back into tty queue
  225.  */
  226. static void
  227. pushback(string)
  228.     Char   *string;
  229. {
  230.     register Char *p;
  231.     char    c;
  232. #ifdef TERMIO
  233. # ifdef POSIX
  234.     struct termios tty, tty_normal;
  235. # else
  236.     struct termio tty, tty_normal;
  237. # endif /* POSIX */
  238. #else
  239.     struct sgttyb tty, tty_normal;
  240. #endif /* TERMIO */
  241.  
  242. #ifdef BSDSIGS
  243.     sigmask_t omask = sigblock(sigmask(SIGINT));
  244. #else
  245.     sighold(SIGINT);
  246. #endif /* BSDSIGS */
  247.  
  248. #ifdef TERMIO
  249. # ifdef POSIX
  250.     (void) tcgetattr(SHOUT, &tty);
  251. # else
  252.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  253. # endif /* POSIX */
  254.     tty_normal = tty;
  255.     tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
  256. # ifdef POSIX
  257.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  258. # else
  259.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  260. # endif /* POSIX */
  261.  
  262.     for (p = string; c = *p; p++)
  263.     (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  264. # ifdef POSIX
  265.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  266. # else
  267.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  268. # endif /* POSIX */
  269.     (void) sigsetmask(omask);
  270. #else
  271.     (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty);
  272.     tty_normal = tty;
  273.     tty.sg_flags &= ~ECHO;
  274.     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty);
  275.  
  276.     for (p = string; c = *p; p++)
  277.     (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  278.     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal);
  279. #endif /* TERMIO */
  280.  
  281. # ifdef BSDSIGS
  282.     (void) sigsetmask(omask);
  283. # else
  284.     (void) sigrelse(SIGINT);
  285. # endif /* BSDISGS */
  286. }
  287.  
  288. /*
  289.  * Concatenate src onto tail of des.
  290.  * Des is a string whose maximum length is count.
  291.  * Always null terminate.
  292.  */
  293. static void
  294. catn(des, src, count)
  295.     register Char *des, *src;
  296.     register count;
  297. {
  298.     while (--count >= 0 && *des)
  299.     des++;
  300.     while (--count >= 0)
  301.     if ((*des++ = *src++) == 0)
  302.         return;
  303.     *des = '\0';
  304. }
  305.  
  306. /*
  307.  * Like strncpy but always leave room for trailing \0
  308.  * and always null terminate.
  309.  */
  310. static void
  311. copyn(des, src, count)
  312.     register Char *des, *src;
  313.     register count;
  314. {
  315.     while (--count >= 0)
  316.     if ((*des++ = *src++) == 0)
  317.         return;
  318.     *des = '\0';
  319. }
  320.  
  321. static  Char
  322. filetype(dir, file)
  323.     Char   *dir, *file;
  324. {
  325.     Char    path[MAXPATHLEN];
  326.     struct stat statb;
  327.  
  328.     catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
  329.     if (lstat(short2str(path), &statb) == 0) {
  330.     switch (statb.st_mode & S_IFMT) {
  331.     case S_IFDIR:
  332.         return ('/');
  333.  
  334.     case S_IFLNK:
  335.         if (stat(short2str(path), &statb) == 0 &&    /* follow it out */
  336.         S_ISDIR(statb.st_mode))
  337.         return ('>');
  338.         else
  339.         return ('@');
  340.  
  341.     case S_IFSOCK:
  342.         return ('=');
  343.  
  344.     default:
  345.         if (statb.st_mode & 0111)
  346.         return ('*');
  347.     }
  348.     }
  349.     return (' ');
  350. }
  351.  
  352. static struct winsize win;
  353.  
  354. /*
  355.  * Print sorted down columns
  356.  */
  357. static void
  358. print_by_column(dir, items, count)
  359.     Char   *dir, *items[];
  360.     int     count;
  361. {
  362.     register int i, rows, r, c, maxwidth = 0, columns;
  363.  
  364.     if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
  365.     win.ws_col = 80;
  366.     for (i = 0; i < count; i++)
  367.     maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
  368.     maxwidth += 2;        /* for the file tag and space */
  369.     columns = win.ws_col / maxwidth;
  370.     if (columns == 0)
  371.     columns = 1;
  372.     rows = (count + (columns - 1)) / columns;
  373.     for (r = 0; r < rows; r++) {
  374.     for (c = 0; c < columns; c++) {
  375.         i = c * rows + r;
  376.         if (i < count) {
  377.         register int w;
  378.  
  379.         xprintf("%s", short2str(items[i]));
  380.         xputchar(dir ? filetype(dir, items[i]) : ' ');
  381.         if (c < columns - 1) {    /* last column? */
  382.             w = Strlen(items[i]) + 1;
  383.             for (; w < maxwidth; w++)
  384.             xputchar(' ');
  385.         }
  386.         }
  387.     }
  388.     xputchar('\r');
  389.     xputchar('\n');
  390.     }
  391. }
  392.  
  393. /*
  394.  * Expand file name with possible tilde usage
  395.  *    ~person/mumble
  396.  * expands to
  397.  *    home_directory_of_person/mumble
  398.  */
  399. static Char *
  400. tilde(new, old)
  401.     Char   *new, *old;
  402. {
  403.     register Char *o, *p;
  404.     register struct passwd *pw;
  405.     static Char person[40];
  406.  
  407.     if (old[0] != '~')
  408.     return (Strcpy(new, old));
  409.  
  410.     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
  411.     *p = '\0';
  412.     if (person[0] == '\0')
  413.     (void) Strcpy(new, value(STRhome));
  414.     else {
  415.     pw = getpwnam(short2str(person));
  416.     if (pw == NULL)
  417.         return (NULL);
  418.     (void) Strcpy(new, str2short(pw->pw_dir));
  419.     }
  420.     (void) Strcat(new, o);
  421.     return (new);
  422. }
  423.  
  424. /*
  425.  * Cause pending line to be printed
  426.  */
  427. static void
  428. retype()
  429. {
  430. #ifdef TERMIO
  431. # ifdef POSIX
  432.     struct termios tty;
  433.  
  434.     (void) tcgetattr(SHOUT, &tty);
  435. # else
  436.     struct termio tty;
  437.  
  438.     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty);
  439. # endif /* POSIX */
  440.  
  441.     tty.c_lflag |= PENDIN;
  442.  
  443. # ifdef POSIX
  444.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  445. # else
  446.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  447. # endif /* POSIX */
  448. #else
  449.     int     pending_input = LPENDIN;
  450.  
  451.     (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input);
  452. #endif /* TERMIO */
  453. }
  454.  
  455. static void
  456. beep()
  457. {
  458.     if (adrof(STRnobeep) == 0)
  459.     (void) write(SHOUT, "\007", 1);
  460. }
  461.  
  462. /*
  463.  * Erase that silly ^[ and
  464.  * print the recognized part of the string
  465.  */
  466. static void
  467. print_recognized_stuff(recognized_part)
  468.     Char   *recognized_part;
  469. {
  470.     /* An optimized erasing of that silly ^[ */
  471.     putraw('\b');
  472.     putraw('\b');
  473.     switch (Strlen(recognized_part)) {
  474.  
  475.     case 0:            /* erase two Characters: ^[ */
  476.     putraw(' ');
  477.     putraw(' ');
  478.     putraw('\b');
  479.     putraw('\b');
  480.     break;
  481.  
  482.     case 1:            /* overstrike the ^, erase the [ */
  483.     xprintf("%s", short2str(recognized_part));
  484.     putraw(' ');
  485.     putraw('\b');
  486.     break;
  487.  
  488.     default:            /* overstrike both Characters ^[ */
  489.     xprintf("%s", short2str(recognized_part));
  490.     break;
  491.     }
  492.     flush();
  493. }
  494.  
  495. /*
  496.  * Parse full path in file into 2 parts: directory and file names
  497.  * Should leave final slash (/) at end of dir.
  498.  */
  499. static void
  500. extract_dir_and_name(path, dir, name)
  501.     Char   *path, *dir, *name;
  502. {
  503.     register Char *p;
  504.  
  505.     p = Strrchr(path, '/');
  506.     if (p == NULL) {
  507.     copyn(name, path, MAXNAMLEN);
  508.     dir[0] = '\0';
  509.     }
  510.     else {
  511.     copyn(name, ++p, MAXNAMLEN);
  512.     copyn(dir, path, p - path);
  513.     }
  514. }
  515.  
  516. static Char *
  517. getentry(dir_fd, looking_for_lognames)
  518.     DIR    *dir_fd;
  519.     int     looking_for_lognames;
  520. {
  521.     register struct passwd *pw;
  522.     register struct dirent *dirp;
  523.  
  524.     if (looking_for_lognames) {
  525.     if ((pw = getpwent()) == NULL)
  526.         return (NULL);
  527.     return (str2short(pw->pw_name));
  528.     }
  529.     if (dirp = readdir(dir_fd))
  530.     return (str2short(dirp->d_name));
  531.     return (NULL);
  532. }
  533.  
  534. static void
  535. free_items(items)
  536.     register Char **items;
  537. {
  538.     register int i;
  539.  
  540.     for (i = 0; items[i]; i++)
  541.     xfree((ptr_t) items[i]);
  542.     xfree((ptr_t) items);
  543. }
  544.  
  545. #ifdef BSDSIGS
  546. # define FREE_ITEMS(items) { \
  547.     sigmask_t omask;\
  548. \
  549.     omask = sigblock(sigmask(SIGINT));\
  550.     free_items(items);\
  551.     items = NULL;\
  552.     (void) sigsetmask(omask);\
  553. }
  554. #else
  555. # define FREE_ITEMS(items) { \
  556.     (void) sighold(SIGINT);\
  557.     free_items(items);\
  558.     items = NULL;\
  559.     (void) sigrelse(SIGINT);\
  560. }
  561. #endif /* BSDSIGS */
  562.  
  563. /*
  564.  * Perform a RECOGNIZE or LIST command on string "word".
  565.  */
  566. static int
  567. tsearch(word, command, max_word_length)
  568.     Char   *word;
  569.     int     max_word_length;
  570.     COMMAND command;
  571. {
  572.     static Char **items = NULL;
  573.     register DIR *dir_fd;
  574.     register numitems = 0, ignoring = TRUE, nignored = 0;
  575.     register name_length, looking_for_lognames;
  576.     Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
  577.     Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
  578.     Char   *entry;
  579.  
  580. #define MAXITEMS 1024
  581.  
  582.     if (items != NULL)
  583.     FREE_ITEMS(items);
  584.  
  585.     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
  586.     if (looking_for_lognames) {
  587.     (void) setpwent();
  588.     copyn(name, &word[1], MAXNAMLEN);    /* name sans ~ */
  589.     dir_fd = NULL;
  590.     }
  591.     else {
  592.     extract_dir_and_name(word, dir, name);
  593.     if (tilde(tilded_dir, dir) == 0)
  594.         return (0);
  595.     dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
  596.     if (dir_fd == NULL)
  597.         return (0);
  598.     }
  599.  
  600. again:                /* search for matches */
  601.     name_length = Strlen(name);
  602.     for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames);) {
  603.     if (!is_prefix(name, entry))
  604.         continue;
  605.     /* Don't match . files on null prefix match */
  606.     if (name_length == 0 && entry[0] == '.' &&
  607.         !looking_for_lognames)
  608.         continue;
  609.     if (command == LIST) {
  610.         if (numitems >= MAXITEMS) {
  611.         xprintf("\nYikes!! Too many %s!!\n",
  612.             looking_for_lognames ?
  613.             "names in password file" : "files");
  614.         break;
  615.         }
  616.         if (items == NULL)
  617.         items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS);
  618.         items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) *
  619.                            sizeof(Char));
  620.         copyn(items[numitems], entry, MAXNAMLEN);
  621.         numitems++;
  622.     }
  623.     else {            /* RECOGNIZE command */
  624.         if (ignoring && ignored(entry))
  625.         nignored++;
  626.         else if (recognize(extended_name,
  627.                    entry, name_length, ++numitems))
  628.         break;
  629.     }
  630.     }
  631.     if (ignoring && numitems == 0 && nignored > 0) {
  632.     ignoring = FALSE;
  633.     nignored = 0;
  634.     if (looking_for_lognames)
  635.         (void) setpwent();
  636.     else
  637.         rewinddir(dir_fd);
  638.     goto again;
  639.     }
  640.  
  641.     if (looking_for_lognames)
  642.     (void) endpwent();
  643.     else
  644.     (void) closedir(dir_fd);
  645.     if (numitems == 0)
  646.     return (0);
  647.     if (command == RECOGNIZE) {
  648.     if (looking_for_lognames)
  649.         copyn(word, STRtilde, 1);
  650.     else
  651.         /* put back dir part */
  652.         copyn(word, dir, max_word_length);
  653.     /* add extended name */
  654.     catn(word, extended_name, max_word_length);
  655.     return (numitems);
  656.     }
  657.     else {            /* LIST */
  658.     qsort((ptr_t) items, numitems, sizeof(items[0]), sortscmp);
  659.     print_by_column(looking_for_lognames ? NULL : tilded_dir,
  660.             items, numitems);
  661.     if (items != NULL)
  662.         FREE_ITEMS(items);
  663.     }
  664.     return (0);
  665. }
  666.  
  667. /*
  668.  * Object: extend what user typed up to an ambiguity.
  669.  * Algorithm:
  670.  * On first match, copy full entry (assume it'll be the only match)
  671.  * On subsequent matches, shorten extended_name to the first
  672.  * Character mismatch between extended_name and entry.
  673.  * If we shorten it back to the prefix length, stop searching.
  674.  */
  675. static int
  676. recognize(extended_name, entry, name_length, numitems)
  677.     Char   *extended_name, *entry;
  678.     int     name_length, numitems;
  679. {
  680.     if (numitems == 1)        /* 1st match */
  681.     copyn(extended_name, entry, MAXNAMLEN);
  682.     else {            /* 2nd & subsequent matches */
  683.     register Char *x, *ent;
  684.     register int len = 0;
  685.  
  686.     x = extended_name;
  687.     for (ent = entry; *x && *x == *ent++; x++, len++);
  688.     *x = '\0';        /* Shorten at 1st Char diff */
  689.     if (len == name_length)    /* Ambiguous to prefix? */
  690.         return (-1);    /* So stop now and save time */
  691.     }
  692.     return (0);
  693. }
  694.  
  695. /*
  696.  * Return true if check matches initial Chars in template.
  697.  * This differs from PWB imatch in that if check is null
  698.  * it matches anything.
  699.  */
  700. static int
  701. is_prefix(check, template)
  702.     register Char *check, *template;
  703. {
  704.     do
  705.     if (*check == 0)
  706.         return (TRUE);
  707.     while (*check++ == *template++);
  708.     return (FALSE);
  709. }
  710.  
  711. /*
  712.  *  Return true if the Chars in template appear at the
  713.  *  end of check, I.e., are it's suffix.
  714.  */
  715. static int
  716. is_suffix(check, template)
  717.     Char   *check, *template;
  718. {
  719.     register Char *c, *t;
  720.  
  721.     for (c = check; *c++;);
  722.     for (t = template; *t++;);
  723.     for (;;) {
  724.     if (t == template)
  725.         return 1;
  726.     if (c == check || *--t != *--c)
  727.         return 0;
  728.     }
  729. }
  730.  
  731. int
  732. tenex(inputline, inputline_size)
  733.     Char   *inputline;
  734.     int     inputline_size;
  735. {
  736.     register int numitems, num_read;
  737.     char    tinputline[BUFSIZ];
  738.  
  739.  
  740.     setup_tty(ON);
  741.  
  742.     while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) {
  743.     int     i;
  744.     static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
  745.     '>', '(', ')', '|', '^', '%', '\0'};
  746.     register Char *str_end, *word_start, last_Char, should_retype;
  747.     register int space_left;
  748.     COMMAND command;
  749.  
  750.     for (i = 0; i < num_read; i++)
  751.         inputline[i] = (unsigned char) tinputline[i];
  752.     last_Char = inputline[num_read - 1] & ASCII;
  753.  
  754.     if (last_Char == '\n' || num_read == inputline_size)
  755.         break;
  756.     command = (last_Char == ESC) ? RECOGNIZE : LIST;
  757.     if (command == LIST)
  758.         xputchar('\n');
  759.     str_end = &inputline[num_read];
  760.     if (last_Char == ESC)
  761.         --str_end;        /* wipeout trailing cmd Char */
  762.     *str_end = '\0';
  763.     /*
  764.      * Find LAST occurence of a delimiter in the inputline. The word start
  765.      * is one Character past it.
  766.      */
  767.     for (word_start = str_end; word_start > inputline; --word_start)
  768.         if (Strchr(delims, word_start[-1]))
  769.         break;
  770.     space_left = inputline_size - (word_start - inputline) - 1;
  771.     numitems = tsearch(word_start, command, space_left);
  772.  
  773.     if (command == RECOGNIZE) {
  774.         /* print from str_end on */
  775.         print_recognized_stuff(str_end);
  776.         if (numitems != 1)    /* Beep = No match/ambiguous */
  777.         beep();
  778.     }
  779.  
  780.     /*
  781.      * Tabs in the input line cause trouble after a pushback. tty driver
  782.      * won't backspace over them because column positions are now
  783.      * incorrect. This is solved by retyping over current line.
  784.      */
  785.     should_retype = FALSE;
  786.     if (Strchr(inputline, '\t')) {    /* tab Char in input line? */
  787.         back_to_col_1();
  788.         should_retype = TRUE;
  789.     }
  790.     if (command == LIST)    /* Always retype after a LIST */
  791.         should_retype = TRUE;
  792.     if (should_retype)
  793.         printprompt();
  794.     pushback(inputline);
  795.     if (should_retype)
  796.         retype();
  797.     }
  798.     setup_tty(OFF);
  799.     return (num_read);
  800. }
  801.  
  802. static int
  803. ignored(entry)
  804.     register Char *entry;
  805. {
  806.     struct varent *vp;
  807.     register Char **cp;
  808.  
  809.     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
  810.     return (FALSE);
  811.     for (; *cp != NULL; cp++)
  812.     if (is_suffix(entry, *cp))
  813.         return (TRUE);
  814.     return (FALSE);
  815. }
  816. #endif    /* FILEC */
  817.